from django.shortcuts import render
from django.shortcuts import HttpResponse
from django.views import View
from django.db.models import F
from django.http import JsonResponse
from django.db import transaction
from home import models
from common.pagination import PageQuerySet
from common.redis_pool import redis_conn
from common.celery_task.celery import app
from common.logger import log
import json



class Home(View):
    '''首页'''

    def get(self, request):
        # 获取所有分类对象，导航栏分类和标签打印
        category_list = models.Category.objects.all()

        # 获取所有文章对象，分页使用
        article_list = models.Article.objects.all()

        '''
        前端用户如果触发选择标签筛选动作，
        需要按照标签对象，过滤出某个标签下的所有文章
        '''
        tag_name = request.GET.get('tag_username')
        if tag_name:
            tag_obj = models.Tag.objects.filter(name=tag_name).first()
            article_list = tag_obj.article_set.all()

        # 拿到分页数据
        obj = PageQuerySet(request, article_list)
        page_queryset, page_obj = obj.get_page_queryset()

        # 最新发布和热门文章排行榜
        recent_article_list = Sidebar().recent_article()
        top_article_obj = Sidebar().top_article()

        return render(
            request, 'templates/home/home.html',
            {'page_queryset': page_queryset,
             'page_obj': page_obj,
             'category_list': category_list,
             'recent_article_list': recent_article_list,
             'top_article_obj': top_article_obj}
        )


class Sidebar():
    '''侧边栏数据
        1、最新发布文章对象列表
        2、热门文章对象列表
    '''

    def recent_article(self):
        # 最新发布文章
        recent_article_list = models.Article.objects.order_by(
            '-create_time'
        ).all()[0:5]
        return recent_article_list

    def top_article(self):
        # 热门文章
        top_article_list = redis_conn.zrevrange(
            'article_visitor_num', 0, 4
        )

        top_article_obj = []
        for article_id in top_article_list:
            article_obj = models.Article.objects.filter(
                pk=int(article_id)
            ).first()
            top_article_obj.append(article_obj)

        return top_article_obj


class ArtivleDetail(View):

    def get(self, request, username, article_id, **kwargs):
        '''文章详情'''
        article_obj = models.Article.objects.filter(
            pk= article_id).first()

        '''
        redis缓存访问量(持久化)，每点击一次，文章访问量+1
        zincrby操作时，key或者member不存在，则自动创建
        缓存粒度--->member 为文章id
        有序集合
        '''
        redis_conn.zincrby('article_visitor_num', 1, article_id)
        # 缓存访问量发生变化的文章id，定时任务需要识别
        ret=redis_conn.sadd('update_id', article_id)

        # 获取文章访问量数据，score是float类型
        article_visitor_num = int(
            redis_conn.zscore('article_visitor_num', article_id)
        )

        # 评论queryset，前端渲染评论需要
        comment_list = models.Comment.objects.filter(article_id= article_id)

        # 最新发布和热门文章排行榜
        recent_article_list = Sidebar().recent_article()
        top_article_obj = Sidebar().top_article()

        return render(
            request, 'home/article_detail.html',
            {'article_obj': article_obj,
            'comment_list': comment_list,
            'article_visitor_num': article_visitor_num,
            'recent_article_list': recent_article_list,
            'top_article_obj': top_article_obj}
        )


@app.task
def crontab_redis():
    '''
    定时任务函数：
        作用：将redis数据更新到mysql中
        逻辑：
            1、集合"update_id"，存储了访问量发生变化的文章的id值，
            2、某个时间触发定时任务，scard('update_id')获取有多少文章被更新
            3、循环spop('update_id')随机删除并拿到文章id
            4、然后redis_conn.zscore('article_visitor_num', tmp_article_id)拿到访问量
            5、再更新数据库

    '''
    member_num = redis_conn.scard('update_id')
    if member_num:

        for i in range(member_num):
            tmp_article_id = redis_conn.spop('update_id')

            article_visitor_num = int(
                redis_conn.zscore('article_visitor_num', tmp_article_id)
            )
            models.Article.objects.filter(
                pk=tmp_article_id
            ).update(visitor_num=article_visitor_num)



class UpOrDown(View):
    '''
    逻辑：
        1、未登录用户不能点赞点踩
        2、自己不能给自己的文章点赞点踩
        3、判断用户是否已点过赞或点过踩
        4、判断行为是点赞还是点踩
        5、修改数据库
    '''

    def __init__(self):
        self.back_dic = {'code': 1000, 'msg': ''}

    def post(self, request):

        article_id = request.POST.get('article_id')
        # 前端传过来的是字符串类，需转成bool类型(True,False)
        is_up = json.loads(
            request.POST.get('is_up')
            )

        article_obj = models.Article.objects.filter(
            pk= article_id
            ).first()

        if self._is_authenticated(request):

            if self._is_article(request,article_obj):

                if self._is_click(request, article_obj):

                    # 判断用户行为，并操作相关表
                    self._upordown(request, article_id,
                                   article_obj, is_up)

                else:
                    self.back_dic['code'] = 1003
                    self.back_dic['msg'] = '已点过，请不要重复点'
            else:
                self.back_dic['code'] = 1002
                self.back_dic['msg'] = '不能给自己点'
        else:
            self.back_dic['code'] = 1001
            self.back_dic['msg'] = '请先<a href="/user/login/">登录</a>'

        return JsonResponse(self.back_dic)



    def _is_authenticated(self, request):
        '''判断用户是否登录'''

        if request.user.is_authenticated:
            return True
        return False

    def _is_article(self, request, article_obj):
        '''判断当前文章是否是当前用户自己的'''

        if not article_obj.blog.user == request.user:
            return True
        return  False

    def _is_click(self, request, article_obj):
        '''判断用户是否给当前文章点过赞或点过踩'''

        is_click = models.UpAndDown.objects.filter(
            user= request.user,
            article= article_obj
            ).first()

        if not is_click:
            return True
        return False

    def _upordown(self, request, article_id, article_obj, is_up):
        '''
        判断用户行为是点赞还是点踩
        '''

        if is_up:
            # 操作文章表，点赞字段
            models.Article.objects.filter(
                pk= article_id
                ).update(up_num= F('up_num') + 1)

            self.back_dic['msg'] = '点赞成功'

        else:
            # 操作文章表，点踩字段
            models.Article.objects.filter(
                pk= article_id
                ).update(down_num= F('down_num') + 1)

            self.back_dic['msg'] = '点踩成功'

        # 操作点赞点踩表
        models.UpAndDown.objects.create(
            user= request.user,
            article= article_obj,
            is_up= is_up
        )


class Comment(View):
    '''文章评论
    子评论，直接关联对应的父评论
    评论自关联
    '''

    def __init__(self):
        self.back_dic = {
            'code': 1000, 'msg': ''
        }

    def post(self, request):

        article_id = request.POST.get('article_id')
        content = request.POST.get('content')
        parent_id = request.POST.get('parent_id')

        if self._is_authenticated(request):
            '''
            开启事务
                操作文章表（comment_num字段）和评论表
            '''
            try:
                with transaction.atomic():
                    models.Article.objects.filter(pk=article_id).update(
                        comment_num=F('comment_num') + 1
                    )

                    models.Comment.objects.create(
                        user=request.user,
                        article_id=article_id,
                        content=content,
                        parent_id=parent_id
                    )

            except Exception as e:
                log.error('文章评论数据创建数据失败', e)

            self.back_dic['msg'] = '评论成功'

        else:
            self.back_dic['code'] = 1001
            self.back_dic['msg'] = '用户未登录'

        return JsonResponse(self.back_dic)

    def _is_authenticated(self, request):
        '''判断用户是否登录'''

        if request.user.is_authenticated:
            return True
        return False








